package chapter_08;

// 继承

/*
1. 继承的基础
子类不能访问超类中被声明成private的成员。
超类变量可以引用子类对象。

2. 使用super
子类可以调用超类中定义的构造函数方法，用super的下面形式：
super(parameter-list);
这里，parameter-list定义了超类中构造函数所用到的所有参数。super( )必须是在子类
构造函数中的第一个执行语句。

3. 何时调用构造函数

4. 方法重载
类层次结构中，如果子类中的一个方法与它超类中的方法有相同的方法名和类型声明，
称子类中的方法重载（override）超类中的方法。从子类中调用重载方法时，它总是引用子
类定义的方法。而超类中定义的方法将被隐藏。

5. 动态方法调度
当一个重载方法通过超类引用被调用，Java根据当前被引用对象的类型来决定执行哪个版本的方法。如果引用的对象类
型不同，就会调用一个重载方法的不同版本。换句话说，是被引用对象的类型（而不是引用变量的类型）决定执行哪个
版本的重载方法。因此，如果超类包含一个被子类重载的方法，那么当通过超类引用变量引用不同对象类型时，就会执
行该方法的不同版本。

6. 使用抽象类
abstract type name(parameter-list)
任何含有一个或多个抽象方法的类都必须声明成抽象类。

7. 继承中使用final
final关键字有三个用途。第一，它可以用来创建一个已命名常量的等价物。final的其他两个用法是应用于继承的。
A. 使用final阻止重载
B. 使用final阻止继承

8. Object类
* Object类是所有类的超类。
* Object clone()					创建一个和被复制的对象完全一样的新对象
* boolean equals(Object object)	    判断对象是否相等
* void finalize()					在一个不常用的对象被使用前调用
* Class getClass()					获取运行时一个对象的类
* int hashCode()					返回调用对象有关的散列值
* void notify()					    恢复一个等待调用对象线程的执行
* void notifyAll()					恢复所有等待调用对象线程的执行
* String toString()				    返回描述对象的一个字符串
* void wait()						等待另一个线程的执行
* void wait(long milliseconds)
* void wait(long milliseconds, int nanoseconds)
* getClass()、notify()、notifyAll()和wait()方法被定义成final。你可以重载除这些
* 方法以外的其他方法。
 */

class Box {
    // 8.1
    double width;
    double height;
    double depth;

    Box(Box ob) {
        width = ob.width;
        height = ob.height;
        depth = ob.depth;
    }

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    Box() {
        width = -1; // use -1 to indicate
        height = -1; // an uninitialized
        depth = -1; // box
    }

    Box(double len) {
        width = height = depth = len;
    }

    double volume() {
        return width * height * depth;
    }
}


class BoxWeight extends Box {
    // 8.1
    double weight;
    BoxWeight(double w, double h, double d, double m) {
//        width = w;
//        height = h;
//        depth = d;
//        weight = m;

        // 8.2
        super(w, h, d);
        weight = m;
    }

    BoxWeight(BoxWeight ob) {
        super(ob);
        weight = ob.weight;
    }

    double volume() {
        System.out.println("BoxWeight called.");
        return width * height * depth;
    }
}


abstract class A {
    // 8.6
    abstract void callme();
    // concrete methods are still allowed in abstract classes
    void callmetoo() {
        System.out.println("This is a concrete method.");
    }
}


class B extends A {
    // 8.6
    // B类必须实现callme()方法，除非B类也是抽象类
    void callme() {
        System.out.println("B's implementation of callme.");
    }
}


class FinalClass {
    // Final关键字有三个用途。第一，它可以用来创建一个已命名常量的等价物。
    // Final的其他两个用法是应用于继承的，一个阻止重载，一个是阻止被继承。

    /*
     * 定义成final方法有时可以提高程序性能：编译器可以自由的内嵌调用final方法因为它
     * 知道这些方法不能被子类重载。当一个小的final函数被调用，通常Java编译器可以通过
     * 调用方法的编译代码直接内嵌来备份子程序的字节码，这样减小了与方法调用有关的昂贵
     * 的开销。内嵌仅仅是final方法的一个可选选项。通常，Java在运行时动态的调用方法，
     * 这样叫做后期绑定。然而既然final方法不能被重载，对方法的调用可以在编译时解决，
     * 这叫做早期绑定。
     */
    final void meth() {
        // 如果FinalClass的子类试图重写meth()方法，将不能通过编译。
        System.out.println("这个方法不能被重写");
    }

    // 不光是final可作用于方法，也可以作用于类。
	/*
	 * 防止一个类被继承，只需在类声明前加上final即可。声明一个final类含蓄地宣告了它
	 * 的所有方法也都是final。你可能也会想到，声明一个既是abstract又是final的类是不
	 * 合法的，因为抽象类本身是不完整的，它依靠它的子类提供完整的实现。
	 */
}


public class chapter_08 {

    static void test1() {
        // 8.1
        BoxWeight mybox1 = new BoxWeight(10, 20, 15, 34.3);
        BoxWeight mybox2 = new BoxWeight(2, 3, 4, 0.076);
        double vol;
        vol = mybox1.volume();
        System.out.println("Volume of mybox1 is " + vol);
        System.out.println("Weight of mybox1 is " + mybox1.weight);
        System.out.println();
        vol = mybox2.volume();
        System.out.println("Volume of mybox2 is " + vol);
        System.out.println("Weight of mybox2 is " + mybox2.weight);
    }

    static void test2() {
        // 8.1
        // 超类变量可以引用子类对象
        BoxWeight weightbox = new BoxWeight(3, 5, 7, 8.37);
        Box plainbox = new Box();
        double vol;
        vol = weightbox.volume();
        System.out.println("Volume of weightbox is " + vol);
        System.out.println("Weight of weightbox is " + weightbox.weight);
        System.out.println();
        // assign BoxWeight reference to Box reference
        plainbox = weightbox;
        /*
        理解是引用变量的类型——而不是引用对象的类型——决定了什么成员可以被访问。
        也就是说，当一个子类对象的引用被赋给一个超类引用变量时，你只能访问超类定义的对
        象的那一部分。这是为什么plainbox不能访问weight的原因，甚至是它引用了
        一个BoxWeight对象也不行。
         */
        vol = plainbox.volume(); // OK, volume() defined in Box
        System.out.println("Volume of plainbox is " + vol);
        /* The following statement is invalid because plainbox
        does not define a weight member. */
        // System.out.println("Weight of plainbox is " + plainbox.weight);
    }

    static void test3() {
        // 8.6
        B b = new B();
        b.callme();
        b.callmetoo();
    }

    public static void main(String args[]) {
//        test1();
//        test2();
        test3();
    }
}

